package mcore

import (
	"fmt"
	"gitee.com/dennis-mxx/gcode-common/mexception"
	"reflect"
)

func StructToMap(entity any) map[string]any {
	type_ := reflect.TypeOf(entity)
	kind := type_.Kind()

	if kind == reflect.Map {
		return entity.(map[string]any)
	} else if kind == reflect.Struct {
		result := make(map[string]any)
		val := reflect.ValueOf(entity)
		for i := 0; i < type_.NumField(); i++ {
			field := type_.Field(i)
			fieldName := HumpNaming(field.Name)
			result[fieldName] = val.Field(i).Interface()
		}
		return result

	}
	panic(mexception.NewException(mexception.UnsupportedException, fmt.Sprintf("unsupported convert type %v", kind)))
}
func StructDBTagMap(entity any) map[string]any {
	var value reflect.Value
	if reflect.TypeOf(entity).Kind() == reflect.Ptr {
		value = reflect.Indirect(reflect.ValueOf(entity).Elem())
	} else {
		value = reflect.Indirect(reflect.ValueOf(entity))
	}

	kind := value.Kind()

	if kind == reflect.Map {
		return entity.(map[string]any)
	} else if kind == reflect.Struct {
		mapping := LoadFieldMapping(value.Type(), "db")

		result := make(map[string]any)
		for i := 0; i < value.NumField(); i++ {
			field := value.Type().Field(i)
			dbName := mapping[field.Name]
			if dbName == "" {
				dbName = HumpNaming(field.Name)
			}

			v := reflect.Indirect(value.Field(i))
			if v.IsValid() {
				result[dbName] = v.Interface()
			}

		}
		return result
	}
	panic(mexception.NewException(mexception.UnsupportedException, fmt.Sprintf("unsupported convert type %v", kind)))
}
func LoadStructField(prefix string, t reflect.Type) []string {
	var fieldList []string
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if reflect.Struct == t.Kind() {
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			ct := f.Type

			if ct.Kind() == reflect.Ptr {
				ct = f.Type.Elem()
				if ct.Kind() == reflect.Struct {
					cp := prefix
					if prefix == "" {
						cp = HumpNaming(f.Name)
					} else {
						cp = prefix + "." + HumpNaming(f.Name)
					}
					fieldList = append(fieldList, LoadStructField(cp, ct)...)

				}
			} else {
				if prefix == "" {
					fieldList = append(fieldList, HumpNaming(f.Name))
				} else {
					fieldList = append(fieldList, prefix+"."+HumpNaming(f.Name))
				}
			}
		}
	}
	return fieldList
}

func LoadStructTag(prefix string, tagName string, t reflect.Type) []string {
	var fieldList []string
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if reflect.Struct == t.Kind() {
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			ct := f.Type
			tag := f.Tag.Get(tagName)
			if tag == "" {
				continue
			}

			if ct.Kind() == reflect.Ptr {
				ct = f.Type.Elem()
				if ct.Kind() == reflect.Struct {
					cp := prefix
					if prefix == "" {
						cp = tag
					} else {
						cp = prefix + "." + tag
					}
					fieldList = append(fieldList, LoadStructField(cp, ct)...)

				}
			} else {
				if prefix == "" {
					fieldList = append(fieldList, tag)
				} else {
					fieldList = append(fieldList, prefix+"."+tag)
				}
			}
		}
	}
	return fieldList
}

func HumpNaming(name string) string {
	b := []byte(name)
	var result []byte
	for i := 0; i < len(b); i++ {
		val := b[i]
		if val >= 65 && val <= 90 {
			if i == 0 {
				result = append(result, byte(val+32))
			} else if b[i-1] >= 97 && b[i-1] <= 122 {
				result = append(result, byte(95))
				result = append(result, val+32)
			}
		} else {
			result = append(result, val)
		}

	}
	return string(result)
}
func RemoveHumpNaming(name string) string {
	b := []byte(name)
	var result []byte
	for i, toUppercase := 0, false; i < len(b); i++ {
		val := b[i]

		if val == 95 {
			if !toUppercase {
				toUppercase = true
				continue
			}
		}
		if toUppercase {
			if val >= 97 && val <= 122 {
				result = append(result, val-32)
				toUppercase = false
				continue
			} else {
				result = append(result, byte(95))
			}
			toUppercase = false
		}
		result = append(result, val)

	}
	return string(result)
}

var fieldMapping = make(map[reflect.Type]map[string]map[string]string)
var tagMapping = make(map[reflect.Type]map[string]map[string]string)

func LoadTagMappingByEntity(entity any, tagName string) map[string]string {
	value := reflect.Indirect(reflect.ValueOf(entity))
	return LoadTagMapping(value.Type(), tagName)

}

func LoadTagMapping(_type reflect.Type, tagName string) map[string]string {
	if _type.Kind() != reflect.Struct {
		panic(mexception.NewException(mexception.IllegalParameterException, "type is not struct type"))
	}
	_typeMapping := tagMapping[_type]
	if _typeMapping == nil {
		_typeMapping = map[string]map[string]string{}
		tagMapping[_type] = _typeMapping
	}
	mapping := _typeMapping[tagName]
	if mapping == nil {
		mapping = map[string]string{}
		_typeMapping[tagName] = mapping
		for i := 0; i < _type.NumField(); i++ {
			field := _type.Field(i)
			tagVal := field.Tag.Get(tagName)
			if tagVal == "" {
				panic(mexception.NewException(mexception.NilException, fmt.Sprintf("%s.%s tag[%s] is empty", _type.Name(), field.Name, tagName)))
			}
			mapping[tagVal] = field.Name
		}
	}
	return mapping
}
func LoadFieldMappingByEntity(entity any, tagName string) map[string]string {
	value := reflect.Indirect(reflect.ValueOf(entity))
	return LoadFieldMapping(value.Type(), tagName)

}

func LoadFieldMapping(_type reflect.Type, tagName string) map[string]string {
	if _type.Kind() != reflect.Struct {
		panic(mexception.NewException(mexception.IllegalParameterException, "type is not struct type"))
	}
	_typeMapping := fieldMapping[_type]
	if _typeMapping == nil {
		_typeMapping = map[string]map[string]string{}
		fieldMapping[_type] = _typeMapping
	}
	mapping := _typeMapping[tagName]
	if mapping == nil {
		mapping = map[string]string{}
		_typeMapping[tagName] = mapping
		for i := 0; i < _type.NumField(); i++ {
			field := _type.Field(i)
			tagVal := field.Tag.Get(tagName)
			mapping[field.Name] = tagVal
		}
	}

	return mapping
}
